Skip to content

M13.3: jump-string parser + JumpHost type (FR-56, FR-57)#11

Merged
UnbreakableMJ merged 1 commit into
mainfrom
feature/m13-3-jump-host-parser
May 4, 2026
Merged

M13.3: jump-string parser + JumpHost type (FR-56, FR-57)#11
UnbreakableMJ merged 1 commit into
mainfrom
feature/m13-3-jump-host-parser

Conversation

@UnbreakableMJ

Copy link
Copy Markdown
Contributor

Summary

Adds the public proxy::jump submodule with the [user@]host[:port] parser and the JumpHost type. Re-opened on a clean rebase off main; previous PR #8 was auto-closed when its base branch (M13.2) was deleted by the squash-merge.

New public surface

  • proxy::JumpHost { host, port, user, identity_files }
  • proxy::parse_jump_chain(raw: &str) -> Result<Vec<JumpHost>, AnvilError>
  • proxy::MAX_JUMP_HOPS (= 8, matches OpenSSH READCONF_MAX_DEPTH per FR-57)

Grammar

OpenSSH -J / ProxyJump syntax: [user@]host[:port], comma-separated. Whitespace around commas is tolerated. The literal none (case-insensitive) is rejected with a clear disable sentinel error — callers that recognize it as the FR-59 disable sentinel should detect it before calling.

Tests (16 new)

Single-hop bare, single-hop user@host:port, two-hop chain, whitespace tolerance, empty/whitespace-only string, trailing/double commas, empty user, empty host, invalid port, port out of range, none literal in three case variants, max-length chain accepted, length+1 rejected, struct PartialEq + Clone sanity. Total: 185 lib tests, 0 failures.

Out of scope

  • IPv6 literals ([::1]:22) — rare in ProxyJump strings, not exercised by the listed FRs.
  • Per-hop ssh_config resolution — happens at the chain manager (M13.4) / dispatcher (M13.6) layer.

Test plan

  • CI passes (Linux/macOS/Windows + MSRV 1.88).
  • cargo fmt / cargo clippy --all-targets -- -D warnings clean.
  • cargo test --lib --tests --locked green.

Plan: M13.3 of let-us-plan-on-bright-cosmos.md.

🤖 Generated with Claude Code

Adds the public `proxy::jump` submodule:

JumpHost (struct):
- pub fields: host: String, port: u16, user: Option<String>,
  identity_files: Vec<PathBuf>.
- The chain manager (M13.4) populates identity_files by re-running
  ssh_config::resolve against each hop's name; M13.3 leaves it empty.

parse_jump_chain(raw: &str) -> Result<Vec<JumpHost>, AnvilError>:
- Accepts the OpenSSH `-J` / `ProxyJump` syntax: `[user@]host[:port]`
  comma-separated, whitespace-around-commas tolerated.
- Defaults the port to 22, leaves user as None when omitted.
- Rejects empty strings, trailing/double commas, empty user prefix,
  empty host, invalid port, and the literal `none` (case-insensitive)
  with clear actionable error messages.

MAX_JUMP_HOPS = 8:
- Matches OpenSSH's READCONF_MAX_DEPTH for ProxyJump chains (FR-57).
- Enforced at parse time, not chain-construction time, so callers
  bail out before any network I/O.

IPv6 literals (`[::1]:22`) are not in scope for M13 — none of the
listed FRs require them and the OpenSSH convention is rare in
ProxyJump strings; documented as a follow-up.

Tests: +16 unit tests (single-hop bare, single-hop user@host:port,
two-hop chain, whitespace tolerance, empty-string/whitespace-only,
trailing/double commas, empty user, empty host, invalid port,
out-of-range port, none literal in three case variants, max-length
chain accepted, length+1 chain rejected, struct PartialEq+Clone
sanity). 189 lib tests green, 0 failures.

Plan: M13.3 of let-us-plan-on-bright-cosmos.md. Stacked on M13.2 (PR
#7).
@UnbreakableMJ
UnbreakableMJ merged commit 3c5a0f7 into main May 4, 2026
5 checks passed
@UnbreakableMJ
UnbreakableMJ deleted the feature/m13-3-jump-host-parser branch May 4, 2026 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant